#!/usr/bin/ruby

# Copyright (C) 2017 Open Source Robotics Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# We use 'dl' for Ruby <= 1.9.x and 'fiddle' for Ruby >= 2.0.x
if RUBY_VERSION.split('.')[0] < '2'
  require 'dl'
  require 'dl/import'
  include DL
else
  require 'fiddle'
  require 'fiddle/import'
  include Fiddle
end

require 'date'
require 'optparse'

# Constants.
LIBRARY_NAME = '@log_library_location@'
LIBRARY_VERSION = '@PROJECT_VERSION_FULL@'
COMMON_OPTIONS =
  "  -h [ --help ]              Print this help message.                   \n"\
  "  -v [ --verbose ] LEVEL     Set verbosity level 0-4 (default 1).       \n"\
  "                                                                        \n"\

COMMANDS = { 'log' =>
  "Record and playback Gazebo Transport topics.                        \n\n"\
  "  gz log record|playback [options]                                     \n"\
  "                                                                        \n"\
  "Options:                                                              \n\n" +
  COMMON_OPTIONS
}

SUBCOMMANDS = { 'record' =>
  "Record Gazebo Transport topics.                                     \n\n"\
  "  gz log record [options]                                              \n"\
  "                                                                        \n"\
  "Options:                                                              \n\n"\
  "  --file FILE                Log file name (default <datetime>.tlog).   \n"\
  "  --force                    Overwrite a file if one exists.            \n"\
  "  --pattern REGEX            Regular expression in C++ ECMAScript grammar\n"\
  "                             (Default match all topics).                \n" +
  COMMON_OPTIONS,
                'playback' =>
  "Playback previously recorded Gazebo Transport topics.               \n\n"\
  "  gz log playback [options]                                            \n"\
  "                                                                        \n"\
  "Required Flags:                                                       \n\n"\
  "  --file FILE                Log file name.                             \n"\
  "                                                                        \n"\
  "Options:                                                              \n\n"\
  "  --pattern REGEX            Regular expression in C++ ECMAScript grammar\n"\
  "                             (Default match all topics).                \n"\
  "  --remap FROM:=TO           Rename a topic while playing back.         \n"\
  "                             Use := to separate the two topics.         \n"\
  "  --wait MILLISEC            Integer (milliseconds) for how long to wait \n"\
  "                             between topic advertisement and publishing.\n"\
  "                             Default: 1000 (1 second).\n"+
  "  -f                         Enable fast playback. This will publish    \n"\
  "                             messages without waiting between messages  \n"\
  "                             according to the logged timestamps.        \n"\
  +
  COMMON_OPTIONS
}

#
# Class for the Gazebo Transport Log command line tools.
#
class Cmd

  #
  # Return a structure describing the options.
  #
  def parse(args)
    options = {
      'file' => '',
      'pattern' => '.+',
      'verbose' => 1,
      'wait' => 1000,
      'force' => false,
      'remap' => '',
      'fast' => false
    }

    usage = COMMANDS[args[0]]

    if !SUBCOMMANDS.key?(args[1])
      puts usage
      exit -1
    else
      usage = SUBCOMMANDS[args[1]]
    end

    opt_parser = OptionParser.new do |opts|
      opts.banner = usage

      opts.on('-h', '--help') do
        puts usage
        exit
      end
      opts.on('--file FILE') do |file|
        options['file'] = file
      end
      opts.on('--pattern REGEX') do |regex|
        options['pattern'] = regex
      end
      opts.on('-v LEVEL', '--verbose LEVEL', OptionParser::DecimalInteger) do |level|
        options['verbose'] = level
      end
      opts.on('--wait MILLISEC', OptionParser::DecimalInteger) do |wait|
        options['wait'] = wait
      end
      opts.on('--force') do
        options['force'] = true
      end
      opts.on('--remap FROMTO') do |remap|
        options['remap'] = remap
      end
      opts.on('-f') do
        options['fast'] = true
      end
    end # opt_parser do

    opt_parser.parse!(args)

    options['command'] = args[0]
    options['subcommand'] = args[1]

    # check required flags
    case options['subcommand']
    when 'record'
      if options['file'].length == 0
        options['file'] = Time.now.strftime("%Y%m%d_%H%M%S.tlog")
      end
    when 'playback'
      if options['file'].length == 0
        puts usage
        exit -1
      end
    end

    options
  end # parse()

  def execute(args)
    options = parse(args)

    if LIBRARY_NAME[0] == '/'
      # If the first character is a slash, we'll assume that we've been given an
      # absolute path to the library. This is only used during test mode.
      plugin = LIBRARY_NAME
    else
      # We're assuming that the library path is relative  to the current
      # location of this script.
      plugin = File.expand_path(File.join(File.dirname(__FILE__), LIBRARY_NAME))
    end
    conf_version = LIBRARY_VERSION

    begin
      Importer.dlload plugin
    rescue DLError
      puts "Library error: [#{plugin}] not found."
      exit(-1)
    end

    # TODO(anyone): Add a sanity check for the gz-transport-log version

    begin
      Importer.extern 'int verbosity(int)'
      result = Importer.verbosity(options['verbose'])
      if 0 != result
        exit -1
      end

      case options['subcommand']
      when 'record'
        if options['force'] and File.exist?(options['file'])
          begin
            File.delete(options['file'])
          rescue Exception => e
            STDERR.puts "Unable to delete file#{options['file']} "
              "because #{e.message}."
          end
        end
        Importer.extern 'int recordTopics(const char *, const char *)'
        result = Importer.recordTopics(options['file'], options['pattern'])
      when 'playback'
        Importer.extern 'int playbackTopics(const char *, const char *, int, \\
                         const char *, int)'
        result = Importer.playbackTopics(
          options['file'], options['pattern'], options['wait'],
          options['remap'], options['fast'] ? 1 : 0)
      end

      if result != 0
        exit -1
      end

    rescue
      puts "Library error: Problem running [#{options['command']}]()"
    end # begin
  end # execute
end # class
